package repository

import (
	"errors"
	"gluenet/internal/glue-srvp/adapter/repository/mysql"
	"gluenet/internal/glue-srvp/domain/dependency"
	"gluenet/internal/glue-srvp/domain/entity"
	"gluenet/internal/pkg/infra"
	"gluenet/internal/pkg/infra/prepare"
)

func init() {
	prepare.RegisterRepo(func() {
		repo := NewTagMetaRepo()
		prepare.InjectDependency(func() dependency.TagMetaRepo {
			return repo
		})
	})
}

func NewTagMetaRepo() *TagMeta {
	r := &TagMeta{}
	return r
}

// Meta 是对domain/dependency的MetaRepo的一个实现，具体函数功能请参见接口定义
type TagMeta struct {
	Meta
}

func (c TagMeta) AddTag(obj *entity.Tag, auth infra.AuthInfo) error {
	var (
		affect int64
		err    error
	)

	session := mysql.DB.NewSession()
	defer session.Close()

	if err = session.Begin(); err != nil {
		return err
	}

	if obj.Group.Id == 0 {
		if affect, err = session.InsertOne(&obj.Group); err != nil {
			session.Rollback()
			return err
		}

		if affect == 0 {
			return errors.New("affect row = 0")
		}

		obj.GroupToUser.TagGroupId = obj.Group.Id
		obj.GroupToUser.UserId = auth.UserId
		obj.GroupToUser.GroupId = auth.GroupId

		if _, err = session.InsertOne(&obj.GroupToUser); err != nil {
			session.Rollback()
			return err
		}
	}

	obj.GroupID = obj.Group.Id
	if _, err = session.InsertOne(obj); err != nil {
		session.Rollback()
		return err
	}

	obj.TagToUser.UserId = auth.UserId
	obj.TagToUser.TagId = obj.Id
	obj.TagToUser.GroupId = auth.GroupId
	if _, err = session.InsertOne(&obj.TagToUser); err != nil {
		session.Rollback()
		return err
	}

	if err = session.Commit(); err != nil {
		session.Rollback()
		return err
	}
	return nil
}

func (c TagMeta) DelTag(obj *entity.Tag, auth infra.AuthInfo) error {
	var (
		err error
	)

	obj.TagToUser.TagId = obj.Id
	switch auth.AuthLevel {
	case 0:
	case 1:
		obj.TagToUser.GroupId = auth.GroupId
	case 2:
		obj.TagToUser.UserId = auth.UserId
		obj.TagToUser.GroupId = auth.GroupId
	}

	session := mysql.DB.NewSession()
	defer session.Close()

	if err = session.Begin(); err != nil {
		return err
	}

	affect, err := session.Delete(&obj.TagToUser)
	if err != nil {
		session.Rollback()
		return err
	}
	if affect == 0 {
		return errors.New("affect row: 0")
	}
	if _, err = session.ID(obj.Id).Delete(obj); err != nil {
		session.Rollback()
		return err
	}

	if err = session.Commit(); err != nil {
		session.Rollback()
		return err
	}

	return nil
}

func (c TagMeta) UptTag(obj *entity.Tag, auth infra.AuthInfo) error {
	var (
		ok     bool
		affect int64
		err    error
	)

	obj.TagToUser.TagId = obj.Id
	switch auth.AuthLevel {
	case 0:
	case 1:
		obj.TagToUser.GroupId = auth.GroupId
	case 2:
		obj.TagToUser.UserId = auth.UserId
		obj.TagToUser.GroupId = auth.GroupId
	}

	session := mysql.DB.NewSession()
	defer session.Close()

	if err = session.Begin(); err != nil {
		return err
	}

	if ok, err = session.Get(&obj.TagToUser); err != nil {
		session.Rollback()
		return err
	}
	if !ok {
		session.Rollback()
		return infra.AuthFailed
	}

	if affect, err = session.ID(obj.Id).Update(obj); !ok {
		session.Rollback()
		return err
	}

	if err = session.Commit(); err != nil {
		session.Rollback()
		return err
	}

	if affect == 0 {
		return errors.New("affect row: 0")
	}

	return nil
}

func (c TagMeta) FindTag(objs *[]entity.Tag, auth infra.AuthInfo, query interface{}, args ...interface{}) error {
	var (
		join        = "INNER"
		table       = "label_tag"
		tableToUser = "label_tag_to_user"
		condition   = "label_tag.id=label_tag_to_user.tag_id"
	)

	switch auth.AuthLevel {
	case 2:
		return mysql.DB.Select(table+".*").Join(join, tableToUser, condition).
			Where("label_tag_to_user.user_id=? and label_tag_to_user.group_id=?", auth.UserId, auth.GroupId).Where(query, args).Find(objs)
	case 1:
		return mysql.DB.Select(table+".*").Join(join, tableToUser, condition).
			Where("label_tag_to_user.group_id=?", auth.GroupId).Where(query, args).Find(objs)
	case 0:
		return mysql.DB.Where(query, args).Find(objs)
	default:
		return nil
	}
}

func (c TagMeta) FindTagDetail(objs *[]entity.TagDetail, auth infra.AuthInfo, query interface{}, args ...interface{}) error {
	switch auth.AuthLevel {
	case 2:
		return mysql.DB.Where("tag_user_id=? and tag_user_group_id=?", auth.UserId, auth.GroupId).
			Where(query, args).
			Find(objs)
	case 1:
		return mysql.DB.Where("tag_user_group_id=?", auth.GroupId).
			Where(query, args).
			Find(objs)
	case 0:
		return mysql.DB.Where(query, args).
			Find(objs)
	default:
		return nil
	}
}

func (c TagMeta) FindObjs(obj interface{}, query interface{}, args ...interface{}) error {
	return mysql.DB.Alias("o").Join("INNER", "label_tag_to_obj", "label_tag_to_obj.obj_id = o.id").
		Where(query, args).Find(obj)
}

func (c TagMeta) AddGroup(obj *entity.TagGroup, auth infra.AuthInfo) error {
	var (
		err error
	)

	session := mysql.DB.NewSession()
	defer session.Close()

	if err = session.Begin(); err != nil {
		return err
	}

	if _, err = session.InsertOne(obj); err != nil {
		session.Rollback()
		return err
	}

	obj.TagGroupToUser.UserId = auth.UserId
	obj.TagGroupToUser.TagGroupId = obj.Id
	obj.TagGroupToUser.GroupId = auth.GroupId
	if _, err = session.InsertOne(&obj.TagGroupToUser); err != nil {
		session.Rollback()
		return err
	}

	if err = session.Commit(); err != nil {
		session.Rollback()
		return err
	}
	return nil
}

func (c TagMeta) DelGroup(obj *entity.TagGroup, auth infra.AuthInfo) error {
	var (
		err error
	)

	obj.TagGroupToUser.GroupId = obj.Id
	switch auth.AuthLevel {
	case 0:
	case 1:
		obj.TagGroupToUser.GroupId = auth.GroupId
	case 2:
		obj.TagGroupToUser.UserId = auth.UserId
		obj.TagGroupToUser.GroupId = auth.GroupId
	}

	session := mysql.DB.NewSession()
	defer session.Close()

	if err = session.Begin(); err != nil {
		return err
	}

	affect, err := session.Delete(&obj.TagGroupToUser)
	if err != nil {
		session.Rollback()
		return err
	}
	if affect == 0 {
		return errors.New("affect row: 0")
	}

	if _, err = session.Table("label_tag").Where("group_id = ?", obj.Id).Delete(&entity.Tag{}); err != nil {
		session.Rollback()
		return err
	}

	if _, err = session.ID(obj.Id).Delete(obj); err != nil {
		session.Rollback()
		return err
	}

	if err = session.Commit(); err != nil {
		session.Rollback()
		return err
	}

	return nil
}

func (c TagMeta) UptGroup(obj *entity.TagGroup, auth infra.AuthInfo) error {
	var (
		ok     bool
		affect int64
		err    error
	)

	obj.TagGroupToUser.TagGroupId = obj.Id
	switch auth.AuthLevel {
	case 0:
	case 1:
		obj.TagGroupToUser.GroupId = auth.GroupId
	case 2:
		obj.TagGroupToUser.UserId = auth.UserId
		obj.TagGroupToUser.GroupId = auth.GroupId
	}

	session := mysql.DB.NewSession()
	defer session.Close()

	if err = session.Begin(); err != nil {
		return err
	}

	if ok, err = session.Get(&obj.TagGroupToUser); err != nil {
		session.Rollback()
		return err
	}
	if !ok {
		session.Rollback()
		return infra.AuthFailed
	}

	if affect, err = session.ID(obj.Id).Update(obj); !ok {
		session.Rollback()
		return err
	}

	if err = session.Commit(); err != nil {
		session.Rollback()
		return err
	}

	if affect == 0 {
		return errors.New("affect row: 0")
	}

	return nil
}

func (c TagMeta) FindGroup(objs *[]entity.TagGroup, auth infra.AuthInfo, query interface{}, args ...interface{}) error {
	var (
		join        = "INNER"
		table       = "label_tag_group"
		tableToUser = "label_tag_group_to_user"
		condition   = "label_tag_group.id=label_tag_group_to_user.tag_group_id"
	)

	switch auth.AuthLevel {
	case 2:
		return mysql.DB.Select(table+".*").Join(join, tableToUser, condition).
			Where("user_id=? and group_id=?", auth.UserId, auth.GroupId).Where(query, args).Find(objs)
	case 1:
		return mysql.DB.Select(table+".*").Join(join, tableToUser, condition).
			Where("group_id=?", auth.GroupId).Where(query, args).Find(objs)
	case 0:
		return mysql.DB.Where(query, args).Find(objs)
	default:
		return nil
	}
}
